/**
 *   @file MCTreeHashtableList.h
 */
#ifndef _MCTREEHASHTABLELIST_H_
#define _MCTREEHASHTABLELIST_H_

#include "../MCTreeNode.h"
#include "../MultiChildTree.h"
#include "../../../common/CppMemPool.h"
#include "../../../common/DLHook.h"
#include "../../LinkedHashtableT.h"

namespace cppflib
{

namespace collections
{

namespace tree
{

/**
 *  A custom list for use in MCTreeNode
 */
template <class TYPE>
class MCListT : public ListCppObjT<TYPE>
{
public:

    /**
     *  Add element to tail
     *
     *  @param [out] pHook -- return the hook after adding to tail
     */
    void AddTail(TYPE *pData, cppflib::DLHook *&pHook)
    {
       pHook = this->AddToList(pData, ListCppObjT<TYPE>::ADD2TAIL, 0);
    }

    /**
     *  Add element to head
     *
     *  @param [out] pHook -- return the hook after adding to head
     */
    void AddHead(TYPE *pData, cppflib::DLHook *&pHook)
    {
       pHook = this->AddToList(pData, ListCppObjT<TYPE>::ADD2HEAD, 0);
    }

    /**
     *  Insert element at a particular position (zero based)
     *
     *  @param [out] pHook -- return the hook after inserting
     */
    void Insert(int nIndex, TYPE *pData, cppflib::DLHook *&pHook)
    {
       if (nIndex <= 0 || this->GetCount() == 0) {
          pHook = this->AddToList(pData, ListCppObjT<TYPE>::ADD2HEAD, 0);
       }
       else if (nIndex >= static_cast<int>(this->GetCount())) {
          pHook = this->AddToList(pData, ListCppObjT<TYPE>::ADD2TAIL, 0);
       }
       else {
          pHook = this->AddToList(pData, ListCppObjT<TYPE>::INSERT, nIndex);
       }
    }

    /**
     *  Get next node with a hook
     */
    TYPE * GetNextElement(cppflib::DLHook *pHook)
    {
       cppflib::DLHook *pNext = pHook->GetNextEntry();
       if (pNext == NULL)
          return NULL;

       return static_cast<TYPE*>(this->GetNodePtr(pNext)->pData); 
    }

    /**
     *  Get previous node with a hook
     */
    TYPE * GetPrevElement(cppflib::DLHook *pHook)
    {
       cppflib::DLHook *pPrev = pHook->GetPrevEntry();
       if (pPrev == NULL)
          return NULL;

       return static_cast<TYPE*>(this->GetNodePtr(pPrev)->pData); 
    }

    /**
     *  Remove an element with a hook
     */
    void Remove(cppflib::DLHook *pHook)
    {
       this->RemoveFromList(pHook, 0);
    }
};


/**
 *  A custom LinkedHashtable for MCTreeNode
 */
class MCTreeNodeLinkedHashtable : public LinkedHashtableT<BaseObject, MCTreeNode>
{
private:
   MCListT<BaseObject> * GetKeyList()
   {
      return static_cast<MCListT<BaseObject> *>(pKeyList);
   }

public:

   MCTreeNodeLinkedHashtable(u32_t capacity) : 
        LinkedHashtableT<BaseObject, MCTreeNode>(capacity, new MCListT<BaseObject>())
   {
   }

   /**
    *  Set element
    *
    *  @param [out] pHook -- return the hook after inserting
    */
   void Set(BaseObject *pKey, MCTreeNode *pVal, cppflib::DLHook *&pHook)
   {
      pHook = NULL;
      pHash->Set(pKey, pVal);
      if (pHash->GetCount() > pKeyList->GetCount()) {
         GetKeyList()->AddTail(pKey, pHook);
      }
   }

   /**
    *  Get next node with a hook
    */
   MCTreeNode * GetNextValue(cppflib::DLHook *pHook)
   {
      BaseObject *pNextKey = GetKeyList()->GetNextElement(pHook);
      if (!pNextKey)
         return NULL;

      return pHash->Get(*pNextKey);
   }

   /**
    *  Get previous node with a hook
    */
   MCTreeNode * GetPrevValue(cppflib::DLHook *pHook)
   {
      BaseObject *pPrevKey = GetKeyList()->GetPrevElement(pHook);
      if (!pPrevKey)
         return NULL;

      return pHash->Get(*pPrevKey);
   }

};

}  // end of namespace tree

}  // end of namespace collections

}  // end of namespace cppflib



#endif
